package com.mask.im.business.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.mask.im.business.model.entity.chat.Friendships;
import com.mask.im.business.model.entity.chat.IMGroupChatUserDO;
import com.mask.im.business.model.entity.*;
import com.mask.im.business.mapper.*;
import com.mask.im.business.model.dto.*;
import com.mask.im.business.model.vo.ConditionVO;
import com.mask.im.business.common.model.RestResponse;
import com.mask.im.business.model.vo.WebsiteConfigVO;
import com.mask.im.business.service.ChannelService;
import com.mask.im.business.service.LiveChatService;
import com.mask.im.business.service.RedisService;
import com.mask.im.business.service.UniqueViewService;
import com.mask.im.business.common.util.IpUtil;
import com.mask.im.business.common.util.MaskIMSecurityUtils;
import com.mask.im.business.common.model.IMSendMessage;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.mask.im.business.common.constant.CommonConstant.*;
import static com.mask.im.business.common.constant.RedisConstant.*;
import static com.mask.im.business.enums.UserAreaTypeEnum.getUserAreaType;

@Service
public class LiveChatServiceImpl implements LiveChatService {

	@Autowired
	private FriendshipsMapper friendshipsMapper;

	@Autowired
	private IMGroupChatUserMapper groupChatUserMapper;

	@Autowired
	private IMFriendChatMessageMapper IMFriendChatMessageMapper;

	@Autowired
	private WebsiteConfigMapper websiteConfigMapper;

	@Autowired
	private ChannelService channelService;

	@Autowired
	private SysLoginLogMapper loginLogMapper;

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private RedisService redisService;

	@Autowired
	private UniqueViewService uniqueViewService;

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Autowired
	private IMGroupChatMapper groupChatMapper;

	@Autowired
	private IMGroupChatMessageMapper groupChatMessageMapper;

	@Autowired
	private UserPurseMapper userPurseMapper;


	@SneakyThrows
	@Override
	public RestResponse<LiveChatUserDTO> getUserData() {
		Integer userId = MaskIMSecurityUtils.getUserId();
		//获取好友数量
		CompletableFuture<Integer> friendCount =
				CompletableFuture.supplyAsync(() -> friendshipsMapper.selectCount(new LambdaQueryWrapper<Friendships>()
				.eq(Friendships::getOneUserId, userId)
				.eq(Friendships::getIsFriends, IS_FRIEND)));
		//获取群聊数量
		CompletableFuture<Integer> groupChatCount =
				CompletableFuture.supplyAsync(() -> groupChatUserMapper.selectCount(new LambdaQueryWrapper<IMGroupChatUserDO>()
				.eq(IMGroupChatUserDO::getUserId, userId)
				.eq(IMGroupChatUserDO::getMemberStatus, TRUE)));
		//获取近期活跃
		CompletableFuture<List<ChatHistoryDTO>> liveChatUserDTOS =
				CompletableFuture.supplyAsync(() -> {
					List<ChatHistoryDTO> chatRecords = IMFriendChatMessageMapper.getChatRecords(userId);
					return dateOfGeneration(LocalDate.now().minusDays(1), chatRecords);
				});

		//获取活跃值
		CompletableFuture<BigDecimal> activityValues =
				CompletableFuture.supplyAsync(() -> {
					UserPurse userPurse = userPurseMapper.selectOne(new LambdaQueryWrapper<UserPurse>()
							.eq(UserPurse::getUserId, userId));
					return userPurse.getAmount();
				});
		//获取登入记录
		CompletableFuture<List<LoginRecordsDTO>> loginRecords = CompletableFuture.supplyAsync(() ->
				loginLogMapper.getUserLoginRecords(userId)
		);

		CompletableFuture.allOf(friendCount,groupChatCount,liveChatUserDTOS,activityValues,loginRecords);
		LiveChatUserDTO liveChatUserDTO = new LiveChatUserDTO();
		liveChatUserDTO.setFriendCount(friendCount.get());
		liveChatUserDTO.setAmount(activityValues.get());
		liveChatUserDTO.setGroupChatCount(groupChatCount.get());
		liveChatUserDTO.setChatHistoryDTOList(liveChatUserDTOS.get());
		liveChatUserDTO.setLoginRecordsDTOList(loginRecords.get());
		return RestResponse.ok(liveChatUserDTO);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updateWebsiteConfig(WebsiteConfigVO websiteConfigVO) {

		WebsiteConfig websiteConfig = WebsiteConfig.builder()
				.id(DEFAULT_CONFIG_ID)
				.config(JSON.toJSONString(websiteConfigVO))
				.build();
		websiteConfigMapper.updateById(websiteConfig);
		redisService.del(WEBSITE_CONFIG);

		if (websiteConfigVO.getNotice()!=null){
			List<Channel> allOnlineUser = channelService.getAllOnlineUser();
			IMSendMessage websocketDTO = new IMSendMessage();
			websocketDTO.setCmd(50001);
			websocketDTO.setData(websiteConfigVO.getNotice());
			allOnlineUser.forEach(channel->{
				if (channel!=null){
					channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
				}
			});
		}
	}

	@Override
	public WebsiteConfigDTO getWebsiteConfig() {
		WebsiteConfigDTO websiteConfigDTO;
		Object websiteConfig = redisService.get(WEBSITE_CONFIG);
		if (Objects.nonNull(websiteConfig)) {
			websiteConfigDTO = JSON.parseObject(websiteConfig.toString(), WebsiteConfigDTO.class);
		} else {
			String config = websiteConfigMapper.selectById(DEFAULT_CONFIG_ID).getConfig();
			websiteConfigDTO = JSON.parseObject(config, WebsiteConfigDTO.class);
			redisService.set(WEBSITE_CONFIG, config);
		}
		return websiteConfigDTO;
	}

	@Override
	public RestResponse<String> broadcastContent() {
		WebsiteConfigDTO websiteConfigDTO;
		Object websiteConfig = redisService.get(WEBSITE_CONFIG);
		if (Objects.nonNull(websiteConfig)) {
			websiteConfigDTO = JSON.parseObject(websiteConfig.toString(), WebsiteConfigDTO.class);
		} else {
			String config = websiteConfigMapper.selectById(DEFAULT_CONFIG_ID).getConfig();
			websiteConfigDTO = JSON.parseObject(config, WebsiteConfigDTO.class);
			redisService.set(WEBSITE_CONFIG, config);
		}

		return RestResponse.ok(websiteConfigDTO.getNotice());
	}

	@Override
	public void report() {
		String ipAddress = IpUtil.getIpAddress(request);
		UserAgent userAgent = IpUtil.getUserAgent(request);
		Browser browser = userAgent.getBrowser();
		OperatingSystem operatingSystem = userAgent.getOperatingSystem();
		String uuid = ipAddress + browser.getName() + operatingSystem.getName();
		String md5 = DigestUtils.md5DigestAsHex(uuid.getBytes());
		if (!redisService.sIsMember(UNIQUE_VISITOR, md5)) {
			String ipSource = IpUtil.getIpSource(ipAddress);
			if (StringUtils.isNotBlank(ipSource)) {
				String ipProvince = IpUtil.getIpProvince(ipSource);
				redisService.hIncr(VISITOR_AREA, ipProvince, 1L);
			} else {
				redisService.hIncr(VISITOR_AREA, UNKNOWN, 1L);
			}
			redisService.incr(BLOG_VIEWS_COUNT, 1);
			redisService.sAdd(UNIQUE_VISITOR, md5);
		}
	}

	@Override
	public LiveChatAdminDTO getBlogBackInfo() {
		Object count = redisService.get(BLOG_VIEWS_COUNT);
		Integer userCount = userInfoMapper.selectCount(null);
		Integer selectCount = groupChatMapper.selectCount(null);
		Integer chatMessageCount = IMFriendChatMessageMapper.selectCount(null);
		Integer groupChatMessageCount = groupChatMessageMapper.selectCount(null);
		List<UniqueViewDTO> uniqueViews = uniqueViewService.listUniqueViews();

		return LiveChatAdminDTO.builder()
				.userCount(userCount)
				.viewsCount(Integer.parseInt(count != null ? count.toString() : "0"))
				.groupChatCount(selectCount)
				.chatHistoryCount(chatMessageCount + groupChatMessageCount)
				.uniqueViewDTOs(uniqueViews).build();
	}

	@Override
	public List<UserAreaDTO> listUserAreas(ConditionVO conditionVO) {
		List<UserAreaDTO> userAreaDTOs = new ArrayList<>();
		switch (Objects.requireNonNull(getUserAreaType(conditionVO.getType()))) {
			case USER:
				Object userArea = redisService.get(USER_AREA);
				if (Objects.nonNull(userArea)) {
					userAreaDTOs = JSON.parseObject(userArea.toString(), List.class);
				}
				return userAreaDTOs;
			case VISITOR:
				Map<String, Object> visitorArea = redisService.hGetAll(VISITOR_AREA);
				if (Objects.nonNull(visitorArea)) {
					userAreaDTOs = visitorArea.entrySet().stream()
							.map(item -> UserAreaDTO.builder()
									.name(item.getKey())
									.value(Long.valueOf(item.getValue().toString()))
									.build())
							.collect(Collectors.toList());
				}
				return userAreaDTOs;
			default:
				break;
		}
		return userAreaDTOs;
	}

	@Override
	public WebsiteConfigUserDTO getUsersWebsiteConfig() {
		WebsiteConfigUserDTO websiteConfigUserDTO;
		Object websiteConfig = redisService.get(WEBSITE_CONFIG);
		if (Objects.nonNull(websiteConfig)) {
			websiteConfigUserDTO = JSON.parseObject(websiteConfig.toString(), WebsiteConfigUserDTO.class);
		} else {
			String config = websiteConfigMapper.selectById(DEFAULT_CONFIG_ID).getConfig();
			websiteConfigUserDTO = JSON.parseObject(config, WebsiteConfigUserDTO.class);
			redisService.set(WEBSITE_CONFIG, config);
		}
		return websiteConfigUserDTO;
	}

	@Override
	public RestResponse<String> currentAddress() {
		String ipAddress = IpUtil.getIpAddress(request);
		String ipSource = IpUtil.getIpSource(ipAddress);
		if (ipSource.equals("内网IP|内网IP")){
			return RestResponse.ok("内网IP");
		}else{
			return RestResponse.ok(ipSource.replaceAll("\\|"," "));
		}
	}

	private List<ChatHistoryDTO> dateOfGeneration(LocalDate nowTime, List<ChatHistoryDTO> sourceData){
		Map<String,Integer> dates = new HashMap<>();
		for (int i = 0; i < 15; i++) {
			dates.put(nowTime.minusDays(i).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),0);
		}
		return convertMapToPersonList(sourceData,dates);
	}

	private List<ChatHistoryDTO> convertMapToPersonList(List<ChatHistoryDTO> sourceData, Map<String,Integer> dateOfGeneration){
		sourceData.forEach(item->{
			dateOfGeneration.put(item.getChatHistoryDate(),item.getChatHistoryValue());
		});

		return dateOfGeneration.entrySet().stream().map(item -> {
			String key = item.getKey();
			Integer value = item.getValue();
			ChatHistoryDTO chatHistoryDTO = new ChatHistoryDTO();
			chatHistoryDTO.setChatHistoryDate(key);
			chatHistoryDTO.setChatHistoryValue(value);
			return chatHistoryDTO;
		}).sorted(Comparator.comparing(ChatHistoryDTO::getChatHistoryDate))
				.collect(Collectors.toList());
	}
}
